/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package edu.isi.pegasus.planner.subworkflow.estimator.implementation;
import edu.isi.pegasus.planner.subworkflow.estimator.implementation.ParallelThread;
import edu.isi.pegasus.planner.classes.ADag;
import edu.isi.pegasus.planner.classes.PegasusBag;
import edu.isi.pegasus.planner.dax.ADAG;
import edu.isi.pegasus.planner.subworkflow.estimator.Estimator;
import edu.isi.pegasus.planner.subworkflow.partitioner.DAXAbstraction;
import edu.isi.pegasus.planner.subworkflow.partitioner.SiteAbstraction;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map;

/**
 *
 * @author wchen
 */
public class CriticalPath extends Estimator{

        //maybe you should write this part to Estimator

    private ADag subDag;
    private edu.isi.pegasus.planner.classes.Job headNode;
    private edu.isi.pegasus.planner.classes.Job rearNode;
    private Map pathMap;
    private double longestTime ;

    public CriticalPath(DAXAbstraction bigDax, SiteAbstraction site, ADag mDag, PegasusBag mBag, HashMap mJob2Time)
    {
        super(bigDax, site, mDag, mBag, mJob2Time);
        subDag = bigDax.subDag;
        headNode = new edu.isi.pegasus.planner.classes.Job();
        headNode.jobID = "HEAD";
        rearNode = new edu.isi.pegasus.planner.classes.Job();
        rearNode.jobID = "REAR";
        pathMap = new HashMap<String, Double>();
        pathMap.clear();
//        long startTime = System.currentTimeMillis();

        //addNode();
        addNodeParallel();
//        long endTime = System.currentTimeMillis();
//        System.out.println("Add Node takes: " + (endTime - startTime) + "milliseconds");
        schedule();
        //longestTime =  getLongestTime(headNode.jobID);
        //longestTime = getCriticalPath(rearNode.jobID);
        longestTime = (Double)pathMap.get(headNode.jobID);
//        System.out.println(longestTime + "\n");
    }

    
    private void addNode()
    {
        subDag.add(headNode);
        subDag.add(rearNode);//write it in parallel
        for(Iterator it = subDag.jobIterator(); it.hasNext();)
        {
            edu.isi.pegasus.planner.classes.Job job =
                    (edu.isi.pegasus.planner.classes.Job)it.next();
            if(subDag.getChildren(job.jobID).size() == 0 && !job.jobID.equals(rearNode.jobID))
                subDag.addNewRelation(job.jobID, rearNode.jobID);
            if(subDag.getParents(job.jobID).size() == 0 && !job.jobID.equals(headNode.jobID))
                subDag.addNewRelation(headNode.jobID, job.jobID);
        }
    }
    private void addNodeParallel()
    {
        subDag.add(headNode);
        subDag.add(rearNode);
        int num  = 8;
        int length = subDag.getNoOfJobs();
        ArrayList threadList = new ArrayList();
        for(int i = 0; i < num; i ++)
        {
           threadList.add(new addFakeNode(subDag, i*length/num,(i+1)*length/num,headNode,rearNode));
        }
        for(int i = 0; i < num; i ++)
        {
            addFakeNode afn = (addFakeNode)threadList.get(i);
            afn.getDone();
        }

    }

    private void schedule()
    {
        int num2job = 4;
        ArrayList threads = new ArrayList();
        //pathMap.put(rearNode.jobID, 0.0);//critical
        LinkedList mQueue = new LinkedList<String>();
        mQueue.add(rearNode.jobID);
        
        for(int i = 0; i < num2job; i ++)
        {
            threads.add(new getCriticalPath(subDag, pathMap, i, num2job, mJob2Time, mQueue));
        }
        for(int i = 0; i < num2job; i ++)
        {
            getCriticalPath gcp = (getCriticalPath)threads.get(i);
            gcp.getDone();
        }
        
    }
    private double getLongestTime(String jobID)
    {

        try
        {
            if(pathMap.containsKey(jobID))
                return (Double)pathMap.get(jobID);
            if(jobID.equals(rearNode.jobID))
                return 0.0;
//            String str = (String)job.vdsNS.get(Pegasus.RUNTIME_KEY);
//            if(str==null)str = "0.0";
//           double runtime = Double.parseDouble(str);
            if(!mJob2Time.containsKey(jobID))
                mJob2Time.put(jobID, 0.0);
           double runtime = (Double)mJob2Time.get(jobID);

           double runtime2 = 0.0;
           for(Iterator it = subDag.getChildren(jobID).iterator();it.hasNext();)
           {
//               edu.isi.pegasus.planner.classes.Job child =
//                       (edu.isi.pegasus.planner.classes.Job)it.next();
               String child = (String)it.next();
               double runtime3 = getLongestTime(child);
               if(runtime3 > runtime2)runtime2 = runtime3;

           }
           pathMap.put(jobID, runtime + runtime2);
           return runtime + runtime2;
        }catch (Exception e)
        {
            e.printStackTrace();
        }
        return 0.0;



    }
    private double getCriticalPath(String rearID)
    {
        LinkedList mQueue = new LinkedList<String>();
        mQueue.add(rearID);
        while(!mQueue.isEmpty())
        {
            String jobID = (String)mQueue.getFirst();
            if(pathMap.containsKey(jobID))
            {
                pathMap.remove(jobID);
            }
             if(!mJob2Time.containsKey(jobID))
                mJob2Time.put(jobID, 0.0);
             double runtime = (Double)mJob2Time.get(jobID);
             boolean hasall = true;
             double runtime3 = 0.0;
             double runtime2 = 0.0;
           for(Iterator it = subDag.getChildren(jobID).iterator();it.hasNext();)
           {

               String child = (String)it.next();
               if(!pathMap.containsKey(child))
               {
                   hasall = false;
               }
               else
               {
                   runtime3 = (Double)mJob2Time.get(child);
                   if(runtime3 > runtime2)runtime2 = runtime3;
               }        
           }
             if(hasall)
             {
                 runtime += runtime2;
                 pathMap.put(jobID, runtime);
                 for(Iterator it = subDag.getParents(jobID).iterator();it.hasNext();)
                   {

                       String parent = (String)it.next();
                       if(!mQueue.contains(parent) && !pathMap.containsKey(parent))
                       {
                           mQueue.addFirst(parent);
                       }

                   }
                 mQueue.remove(jobID);
             }
             else
             {
                 mQueue.remove(jobID);mQueue.addLast(jobID);
             }
            
        }
        return (Double)pathMap.get(headNode.jobID);
    }
    public double getEstimatedRuntime(SiteAbstraction newsite)
    {

        ADAG dag = bigDax.adag;
        this.site = newsite;

        
        return longestTime;

    }

}
